import path from 'path';
import webpack from 'webpack';
import AssetsPlugin from 'assets-webpack-plugin';
import nodeExternals from 'webpack-node-externals';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import overrideRules from './lib/overrideRules';
import pkg from '../package.json';

const isDebug = !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose');
const isAnalyze = process.argv.includes('--analyze') || process.argv.includes('--analyse');
const isMap = process.argv.includes('--map');

// Hard choice here...
// You can enforce this for test environments :-)
const REACT_INTL_ENFORCE_DESCRIPTIONS = false;

const reScript = /\.jsx?$/;
const reGraphql = /\.(graphql|gql)$/;
const reStyle = /\.(css|less|scss|sss)$/;
const reImage = /\.(bmp|gif|jpe?g|png|svg)$/;
const staticAssetName = isDebug ? '[path][name].[ext]?[hash:8]' : '[hash:8].[ext]';

export default (dir = 'build1') => {
  //
  // Common configuration chunk to be used for both
  // client-side (client.js) and server-side (server.js) bundles
  // -----------------------------------------------------------------------------

  const config = {
    context: path.resolve(__dirname, '..'),

    output: {
      path: path.resolve(__dirname, `../${dir}/public/assets`),
      publicPath: '/assets/',
      pathinfo: isVerbose,
      filename: isDebug ? '[name].js' : '[name].[chunkhash:8].js',
      chunkFilename: isDebug ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js',
      devtoolModuleFilenameTemplate: info => path.resolve(info.absoluteResourcePath),
    },

    resolve: {
      // Allow absolute paths in imports, e.g. import Button from 'components/Button'
      // Keep in sync with .flowconfig and .eslintrc
      modules: ['node_modules', 'src'],
    },

    module: {
      // Make missing exports an error instead of warning
      strictExportPresence: true,

      rules: [
        // Rules for JS / JSX
        {
          test: reScript,
          include: path.resolve(__dirname, '../src'),
          loader: 'babel-loader',
          options: {
            // https://github.com/babel/babel-loader#options
            cacheDirectory: isDebug,

            // https://babeljs.io/docs/usage/options/
            babelrc: false,
            presets: [
              // A Babel preset that can automatically determine the Babel plugins and polyfills
              // https://github.com/babel/babel-preset-env
              [
                'buildTools.env',
                {
                  targets: {
                    browsers: pkg.browserslist,
                    uglify: true,
                  },
                  modules: false,
                  useBuiltIns: false,
                  debug: false,
                },
              ],
              // Experimental ECMAScript proposals
              // https://babeljs.io/docs/plugins/#presets-stage-x-experimental-presets-
              'stage-2',
              // JSX, Flow
              // https://github.com/babel/babel/tree/master/packages/babel-preset-react
              'react',
              // Optimize React code for the production build
              // https://github.com/thejameskyle/babel-react-optimize
              ...(isDebug ? [] : ['react-optimize']),
            ],
            plugins: [
              // Adds component stack to warning messages
              // https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source
              ...(isDebug ? ['transform-react-jsx-source'] : []),
              // Adds __self attribute to JSX which React will use for some warnings
              // https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-self
              ...(isDebug ? ['transform-react-jsx-self'] : []),
              [
                'react-intl',
                {
                  messagesDir: path.resolve(__dirname, `../${dir}/messages/extracted`),
                  extractSourceLocation: true,
                  enforceDescriptions: REACT_INTL_ENFORCE_DESCRIPTIONS,
                },
              ],
              [
                'import',
                {
                  libraryName: 'antd',
                  /* style: true, */
                },
              ],
              /* [
                'inline-import-graphql-ast'
              ] */
            ],
          },
        },

        // Rules for GraphQL
        {
          test: reGraphql,
          exclude: /node_modules/,
          loader: 'graphql-tag/loader',
        },

        // Rules for Style Sheets
        {
          test: reStyle,
          rules: [
            // Convert CSS into JS module
            {
              issuer: { not: [reStyle] },
              use: 'isomorphic-style-loader',
            },

            // Process external/third-party styles
            /* {
              exclude: path.resolve(__dirname, '../src'),
              loader: 'css-loader',
              options: {
                sourceMap: isDebug,
                minimize: !isDebug,
                discardComments: { removeAll: true },
              },
            }, */

            // Process default antd styles
            {
              include: path.resolve(__dirname, '../node_modules/antd'),
              loader: 'css-loader',
              options: {
                importLoaders: 1,
                sourceMap: isDebug,
                minimize: !isDebug,
                discardComments: { removeAll: true },
              },
            },

            // Process internal/project styles (from src folder)
            // Page grad style
            {
              include: path.resolve(__dirname, '../src'),
              exclude: [path.resolve(__dirname, '../src/components/antdTheme')],
              loader: 'css-loader',
              options: {
                // CSS Loader https://github.com/webpack/css-loader
                importLoaders: 1,
                sourceMap: isDebug,
                // CSS Modules https://github.com/css-modules/css-modules
                modules: true,
                localIdentName: isDebug ? '[name]-[local]-[hash:base64:5]' : '[hash:base64:5]',
                // CSS Nano http://cssnano.co/options/
                minimize: !isDebug,
                discardComments: { removeAll: true },
              },
            },

            // Apply PostCSS plugins including autoprefixer
            {
              loader: 'postcss-loader',
              options: {
                config: {
                  path: './tools/postcss.config.js',
                },
                sourceMap: isDebug,
              },
            },

            // Compile Less to CSS
            // https://github.com/webpack-contrib/less-loader
            // Install dependencies before uncommenting: yarn add --dev less-loader less
            {
              test: /\.less$/,
              loader: 'less-loader',
              options: {
                sourceMap: isDebug,
              },
            },

            // Compile Sass to CSS
            // https://github.com/webpack-contrib/sass-loader
            // Install dependencies before uncommenting: yarn add --dev sass-loader node-sass
            // {
            //   test: /\.scss$/,
            //   loader: 'sass-loader',
            // },
          ],
        },

        // Rules for images
        {
          test: reImage,
          oneOf: [
            // Inline lightweight images into CSS
            {
              issuer: reStyle,
              oneOf: [
                // Inline lightweight SVGs as UTF-8 encoded DataUrl string
                {
                  test: /\.svg$/,
                  loader: 'svg-url-loader',
                  options: {
                    name: staticAssetName,
                    limit: 4096, // 4kb
                  },
                },

                // Inline lightweight images as Base64 encoded DataUrl string
                {
                  loader: 'url-loader',
                  options: {
                    name: staticAssetName,
                    limit: 4096, // 4kb
                  },
                },
              ],
            },

            // Or return public URL to image resource
            {
              loader: 'file-loader',
              options: {
                name: staticAssetName,
              },
            },
          ],
        },

        // Convert plain text into JS module
        {
          test: /\.txt$/,
          loader: 'raw-loader',
        },

        // Convert Markdown into HTML
        {
          test: /\.md$/,
          loader: path.resolve(__dirname, './lib/markdown-loader.js'),
        },

        // Return public URL for all assets unless explicitly excluded
        // DO NOT FORGET to update `exclude` list when you adding a new loader
        {
          exclude: [reScript, reStyle, reImage, reGraphql, /\.json$/, /\.txt$/, /\.md$/],
          loader: 'file-loader',
          options: {
            name: staticAssetName,
          },
        },

        // Exclude dev modules from production build
        ...(isDebug
          ? []
          : [
              {
                test: path.resolve(__dirname, '../node_modules/react-deep-force-update/lib/index.js'),
                loader: 'null-loader',
              },
            ]),
      ],
    },

    // Don't attempt to continue if there are any errors.
    bail: !isDebug,

    cache: isDebug,

    // Specify what bundle information gets displayed
    // https://webpack.js.org/configuration/stats/
    stats: {
      cached: isVerbose,
      cachedAssets: isVerbose,
      chunks: isVerbose,
      chunkModules: isVerbose,
      colors: true,
      hash: isVerbose,
      modules: isVerbose,
      reasons: isDebug,
      timings: true,
      version: isVerbose,
    },

    // Choose a developer tool to enhance debugging
    // https://webpack.js.org/configuration/devtool/#devtool
    devtool: isDebug ? 'cheap-module-inline-source-map' : isMap ? 'source-map' : '',
  };

  //
  // Configuration for the client-side bundle (client.js)
  // -----------------------------------------------------------------------------

  const clientConfig = {
    ...config,

    name: 'client',
    target: 'web',

    entry: {
      client: ['babel-polyfill', './src/clientLoader.js'],
    },

    plugins: [
      // Define free variables
      // https://webpack.js.org/plugins/define-plugin/
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
        'process.env.BROWSER': true,
        __DEV__: isDebug,
      }),

      // Emit a file with assets paths
      // https://github.com/sporto/assets-webpack-plugin#options
      new AssetsPlugin({
        path: path.resolve(__dirname, `../${dir}`),
        filename: 'assets.json',
        prettyPrint: true,
      }),

      // Move modules that occur in multiple entry chunks to a new entry chunk (the commons chunk).
      // https://webpack.js.org/plugins/commons-chunk-plugin/
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        minChunks: module => /node_modules/.test(module.resource),
      }),

      ...(isDebug
        ? []
        : [
            // Decrease script evaluation time
            // https://github.com/webpack/webpack/blob/master/examples/scope-hoisting/README.md
            new webpack.optimize.ModuleConcatenationPlugin(),

            // Minimize all JavaScript output of chunks
            // https://github.com/mishoo/UglifyJS2#compressor-options
            new webpack.optimize.UglifyJsPlugin({
              sourceMap: isMap,
              compress: {
                screw_ie8: true, // React doesn't support IE8
                warnings: isVerbose,
                unused: true,
                dead_code: true,
              },
              mangle: {
                screw_ie8: true,
              },
              output: {
                comments: false,
                screw_ie8: true,
              },
            }),
          ]),

      // Webpack Bundle Analyzer
      // https://github.com/th0r/webpack-bundle-analyzer
      ...(isAnalyze ? [new BundleAnalyzerPlugin()] : []),
    ],

    // Some libraries import Node modules but don't use them in the browser.
    // Tell Webpack to provide empty mocks for them so importing them works.
    // https://webpack.js.org/configuration/node/
    // https://github.com/webpack/node-libs-browser/tree/master/mock
    node: {
      fs: 'empty',
      net: 'empty',
      tls: 'empty',
    },
  };

  //
  // Configuration for the server-side bundle (server.js)
  // -----------------------------------------------------------------------------

  const serverConfig = {
    ...config,

    name: 'server',
    target: 'node',

    entry: {
      server: ['babel-polyfill', './src/server.js'],
    },

    output: {
      ...config.output,
      path: path.resolve(__dirname, `../${dir}`),
      filename: '[name].js',
      chunkFilename: 'chunks/[name].js',
      libraryTarget: 'commonjs2',
    },

    // Webpack mutates resolve object, so clone it to avoid issues
    // https://github.com/webpack/webpack/issues/4817
    resolve: {
      ...config.resolve,
    },

    module: {
      ...config.module,

      rules: overrideRules(config.module.rules, rule => {
        // Override babel-preset-env configuration for Node.js
        if (rule.loader === 'babel-loader') {
          return {
            ...rule,
            options: {
              ...rule.options,
              presets: rule.options.presets.map(
                preset =>
                  preset[0] !== 'buildTools.env'
                    ? preset
                    : [
                        'buildTools.env',
                        {
                          targets: {
                            node: pkg.engines.node.match(/(\d+\.?)+/)[0],
                          },
                          modules: false,
                          useBuiltIns: false,
                          debug: false,
                        },
                      ],
              ),
            },
          };
        }

        // Override paths to static assets
        if (rule.loader === 'file-loader' || rule.loader === 'url-loader' || rule.loader === 'svg-url-loader') {
          return {
            ...rule,
            options: {
              ...rule.options,
              name: `public/assets/${rule.options.name}`,
              publicPath: url => url.replace(/^public/, ''),
            },
          };
        }

        return rule;
      }),
    },

    externals: [
      './assets.json',
      nodeExternals({
        whitelist: [reStyle, reImage],
      }),
    ],

    plugins: [
      // Define free variables
      // https://webpack.js.org/plugins/define-plugin/
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
        'process.env.BROWSER': false,
        __DEV__: isDebug,
      }),

      // Adds a banner to the top of each generated chunk
      // https://webpack.js.org/plugins/banner-plugin/
      new webpack.BannerPlugin({
        banner: 'require("source-map-support").install();',
        raw: true,
        entryOnly: false,
      }),
    ],

    // Do not replace node globals with polyfills
    // https://webpack.js.org/configuration/node/
    node: {
      console: false,
      global: false,
      process: false,
      Buffer: false,
      __filename: false,
      __dirname: false,
    },
  };

  return [clientConfig, serverConfig];
};
